package main

import (
	"flag"
	"fmt"
	"gitee.com/bluesky2006/ftp/logger"
	"github.com/pkg/sftp"
	"golang.org/x/crypto/ssh"
	"gopkg.in/ini.v1"
	"io/ioutil"
	"log"
	"os"
	"path"
	"regexp"
	"strings"
	"time"
)

func main() {
	//定义命令行参数方式1
	var _cmd string
	var _confpath string
	var _serverPort int
	var _remotepath string
	var _localpath string
	var _ip string

	var (
		user string
		password string
	)

	flag.StringVar(&_confpath, "conf", "conf/app.ini", "配置路径")
	flag.StringVar(&_cmd, "cmd", "ls", "命令:支持get,ls")
	flag.IntVar(&_serverPort, "port", 22, "端口")
	flag.StringVar(&_ip, "ip", "", "服务器IP")
	flag.StringVar(&_remotepath, "rpath", "", "远程路径")
	flag.StringVar(&_localpath, "lpath", "", "本地路径")
	//解析命令行参数
	flag.Parse()
    // ----//---------------
	cfg, err := ini.Load(_confpath)
	if err != nil {
		logger.Fatal("Fail to read file: ", _confpath)
	}
	logger.Debug("App Name:", cfg.Section("").Key("app_name").String())
	loglevel := cfg.Section("").Key("log_level").String()
	logger.Debug("Log Level:", loglevel)
	logger.Initial(loglevel)

	cmds := strings.Split(_cmd," ")
	logger.Infof("cmd:%s length :%d",_cmd,len(cmds))
	//_, err := model.GetAllTaskByExcel()
	//if err != nil {
	//	logger.Fatal("Fail to read excel file: ", err)
	//}
	serverPort, err := cfg.Section("server").Key("port").Int()
	if err != nil {
		log.Fatal(err)
	}

	user = cfg.Section("server").Key("user").String()
	password = cfg.Section("server").Key("password").String()
	remotepath := cfg.Section("server").Key("remotepath").String()
	localpath := cfg.Section("local").Key("localpath").String()
	ip :=  cfg.Section("server").Key("ip").String()
	if _remotepath!= "" {
		remotepath = _remotepath
	}
	if _localpath!= "" {
		localpath = _localpath
	}
	if _ip!= "" {
		ip = _ip
	}
	if _serverPort != 22 {
		serverPort = _serverPort
	}
	logger.Debug("Server IP:",ip )
	logger.Debug("Server Port:", serverPort)
	logger.Debug("Server User:",user )
	logger.Debug("Server Password:", password)
	logger.Debug("Server remote path:", remotepath)
	logger.Debug("local path :", localpath)

	logger.Info("--------------------------------")
	method := strings.ToLower(cmds[0])
    logger.Info("method:",method)
	switch method {
	case "get","mget":
		logger.Info("start to download")
		if len(cmds)>1 {
			DownLoad(ip, serverPort, user, password, localpath, remotepath, cmds[1])
		}else{
			DownLoad(ip, serverPort, user, password, localpath, remotepath, "")
		}
	case "ls":
		logger.Info("start to list files")
		if len(cmds)>1 {
			Ls(ip, serverPort, user, password, remotepath,cmds[1])
		}else{
			Ls(ip, serverPort, user, password, remotepath,"")
		}

	case "put","upload":
		logger.Info("start to upload")
	default:
		logger.Infof("system do not support this command[%s]!",method)
	}
	//	DoBackup("k8s-node5", 22, "zouw", "sa", "/tmp/test01", "/home/zouw/")
	//DownLoad("k8s-node5", 22, "zouw", "sa", "/tmp/test01/xx/", "/home/zouw/", ".*.txt")
	logger.Info("--------------END------------------")
}

//文件显示
func Ls(host string, port int, userName string, password string, remotepath string,pattern string) {
	var (
		err        error
		sftpClient *sftp.Client
	)
	start := time.Now()
	sftpClient, err = connect(userName, password, host, port)
	if err != nil {
		log.Fatal(err)
	}
	defer sftpClient.Close()
	files, err1 := sftpClient.ReadDir(remotepath)
	if err1 != nil {
		log.Fatal(remotepath + " remote path not exists!")
	}

	for i, v := range files {
		if pattern == "" {
			logger.Infof("%d  %s(isDir:%t)", i+1, v.Name(), v.IsDir())
		}else {
			match, _ := regexp.MatchString(pattern, v.Name())
			if  match {
				logger.Infof("%d  %s(isDir:%t)", i+1, v.Name(), v.IsDir())
			}
		}
	}
	elapsed := time.Since(start)
	logger.Debug("elapsed time : ", elapsed)
}

//文件下载
func DownLoad(host string, port int, userName string, password string, localpath string, remotepath string, pattern string) {
	var (
		err        error
		sftpClient *sftp.Client
	)
	start := time.Now()
	sftpClient, err = connect(userName, password, host, port)
	if err != nil {
		log.Fatal(err)
	}
	defer sftpClient.Close()
	files, err1 := sftpClient.ReadDir(remotepath)
	if err1 != nil {
		log.Fatal(remotepath + " remote path not exists!")
	}
	for i, v := range files {
		if pattern == "" {
			logger.Debug("download whole files in directory ",remotepath)
			if !v.IsDir() {
				downLoadFile(sftpClient, localpath, path.Join(remotepath, v.Name()))
			}
		}else {
			match, _ := regexp.MatchString(pattern, v.Name())
			logger.Debugf("%d  %s(isDir:%t) match:%t", i+1, v.Name(), v.IsDir(), match)
			if !v.IsDir() && match {
				logger.Debug(localpath, v.Name())
				downLoadFile(sftpClient, localpath, path.Join(remotepath, v.Name()))
			}
		}
	}
	//downLoadFile(sftpClient, localpath, remotepath)
	elapsed := time.Since(start)
	logger.Debug("elapsed time : ", elapsed)
}

func DoBackup(host string, port int, userName string, password string, localPath string, remotePath string) {
	var (
		err        error
		sftpClient *sftp.Client
	)
	start := time.Now()
	sftpClient, err = connect(userName, password, host, port)
	if err != nil {
		log.Fatal(err)
	}
	defer sftpClient.Close()
	_, errStat := sftpClient.Stat(remotePath)
	if errStat != nil {
		log.Fatal(remotePath + " remote path not exists!")
	}
	backupDirs, err := ioutil.ReadDir(localPath)
	if err != nil {
		log.Fatal(localPath + " local path not exists!")
	}
	logger.Info(backupDirs)
	uploadDirectory(sftpClient, localPath, remotePath)
	elapsed := time.Since(start)
	fmt.Println("elapsed time : ", elapsed)
}

func connect(user, password, host string, port int) (*sftp.Client, error) {
	var (
		auth         []ssh.AuthMethod
		addr         string
		clientConfig *ssh.ClientConfig
		sshClient    *ssh.Client
		sftpClient   *sftp.Client
		err          error
	)
	// get auth method
	auth = make([]ssh.AuthMethod, 0)
	auth = append(auth, ssh.Password(password))
	clientConfig = &ssh.ClientConfig{
		User:            user,
		Auth:            auth,
		Timeout:         30 * time.Second,
		HostKeyCallback: ssh.InsecureIgnoreHostKey(), //ssh.FixedHostKey(hostKey),
	}
	// connet to ssh
	addr = fmt.Sprintf("%s:%d", host, port)
	if sshClient, err = ssh.Dial("tcp", addr, clientConfig); err != nil {
		return nil, err
	}
	// create sftp client
	if sftpClient, err = sftp.NewClient(sshClient); err != nil {
		return nil, err
	}
	return sftpClient, nil
}

func uploadFile(sftpClient *sftp.Client, localFilePath string, remotePath string) {
	srcFile, err := os.Open(localFilePath)
	if err != nil {
		fmt.Println("os.Open error : ", localFilePath)
		log.Fatal(err)
	}
	defer srcFile.Close()
	var remoteFileName = path.Base(localFilePath)
	dstFile, err := sftpClient.Create(path.Join(remotePath, remoteFileName))
	if err != nil {
		fmt.Println("sftpClient.Create error : ", path.Join(remotePath, remoteFileName))
		log.Fatal(err)
	}
	defer dstFile.Close()
	ff, err := ioutil.ReadAll(srcFile)
	if err != nil {
		fmt.Println("ReadAll error : ", localFilePath)
		log.Fatal(err)
	}
	dstFile.Write(ff)
	fmt.Println(localFilePath + " copy file to remote server finished!")
}

//文件下载
func downLoadFile(sftpClient *sftp.Client, localpath string, remotepath string) {
	srcFile, err := sftpClient.Open(remotepath)
	if err != nil {
		logger.Error(remotepath, " 远程文件不存在", err)
		panic(err)
	}
	localFilename := path.Base(remotepath)
	logger.Debugf("localpath:%s,remotepath:%s,localFilename:%s",localpath,remotepath,localFilename)
	filepath := path.Join(localpath, localFilename)
	dstFile, e := os.Create(filepath)
	if e != nil {
		logger.Error("文件创建失败", e)
		panic(e)
	}
	defer dstFile.Close()
	if _, err1 := srcFile.WriteTo(dstFile); err1 != nil {
		logger.Error("文件写入失败", err1)
		panic(err1)
	}
	logger.Info(remotepath, " 下载至 ", filepath)
}

func uploadDirectory(sftpClient *sftp.Client, localPath string, remotePath string) {
	localFiles, err := ioutil.ReadDir(localPath)
	if err != nil {
		log.Fatal("read dir list fail ", err)
	}
	for _, backupDir := range localFiles {
		localFilePath := path.Join(localPath, backupDir.Name())
		remoteFilePath := path.Join(remotePath, backupDir.Name())
		if backupDir.IsDir() {
			sftpClient.Mkdir(remoteFilePath)
			uploadDirectory(sftpClient, localFilePath, remoteFilePath)
		} else {
			uploadFile(sftpClient, path.Join(localPath, backupDir.Name()), remotePath)
		}
	}
	fmt.Println(localPath + " copy directory to remote server finished!")
}
